package testcases;



import java.text.DateFormat;
import java.util.Date;

import junit.framework.TestCase;

import javax.swing.*; //for components

import org.junit.Test;
import juf.*;		//all of JUF
import juf.adapter.*;


/**
 * Tests the basic behavior of the AdapterFactory.createAdapter() method
 * which should return the correct adapter for a given Property 
 * <p/>
 * 
 * @author Dale Halliwell
 *
 */
public class AdapterFactoryTests extends TestCase {

	public static DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
	
	/**Tests createAdapter on a boolean field
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_BoolField() {
				
		Student fixture = new Student(123,"Hello World","", new Date(), true, 0);
		fixture.male = true;
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("male"),fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JCheckBoxAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals(true, ((JCheckBox) a.getDataComponent()).isSelected()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JCheckBox) a.getDataComponent()).setSelected(false);//now try to change the component's value
			a.save(); //call save on the adapter
			assertEquals(false, fixture.male); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	
	
	
	//---------------------------String

	/**Tests createAdapter on a string field
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_StringField() {
				
		Student fixture = new Student(123,"Hello World","", new Date(), true, 0);
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("firstname"),fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("Hello World", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JTextField) a.getDataComponent()).setText("New Text");//now try to change the component's value
			a.save(); //call save on the adapter
			assertEquals("New Text", fixture.firstname); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	
	/**Tests createAdapter on a string getter/setter pair
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_StringMethodPair() {
				
		Student fixture = new Student(123,"MyFirstName","", new Date(), false, 0);
		
		try {
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getFirstname"),fixture.getClass().getMethod("setFirstname",String.class), fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("MyFirstName", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JTextField) a.getDataComponent()).setText("NewName");//now try to change the component's value
			a.save(); //and call save on the adapter
			assertEquals("NewName", fixture.firstname); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	/**Tests createAdapter on a string getter (no setter) - should produce a readonly component
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_StringReadonly() {
				
		Student fixture = new Student(123,"","", new Date(), true, 0);
		
		try {
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getCollegename"), fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("Sciences", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JTextField) a.getDataComponent()).setText("New Text");//now try to change the component's value
			a.save(); //and call save on the adapter
			assertEquals("Sciences", fixture.getCollegename()); //the value in the fixture object should not have changed
			//because the Property should be readonly
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	
	//---------------------------Int

	/**Tests createAdapter on an int field
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_IntField() {
				
		Student fixture = new Student(123,"Hello World","", new Date(), true, 0);
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("studentId"),fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("123", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JFormattedTextField) a.getDataComponent()).setText("234");//now try to change the component's value
			a.save(); //call save on the adapter
			assertEquals(234, fixture.studentId); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
		
	/**Tests createAdapter on a int getter (no setter) - should produce a readonly component
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_IntReadonly() {
				
		Student fixture = new Student(123,"","", new Date() , false, 0);
		
		try {
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getStudentId"), fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("123", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JTextField) a.getDataComponent()).setText("234");//now try to change the component's value
			a.save(); //and call save on the adapter
			assertEquals(123, fixture.getStudentId()); //the value in the fixture object should not have changed
			//because the Property should be readonly
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	//---------------------------Decimal
	
	/**Tests createAdapter on an float field
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_FloatField() {
				
		Student fixture = new Student(123,"Hello World","", new Date(), false, 0);
		fixture.gpa = 4.1F;
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("gpa"),fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("4.1", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JFormattedTextField) a.getDataComponent()).setText("1.5");//now try to change the component's value
			a.save(); //call save on the adapter
			assertEquals(1.5F, fixture.gpa); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	/**Tests createAdapter on a double method-pair
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_DoubleField() {
				
		Student fixture = new Student(123,"Hello World","", new Date(), false, 0);
		fixture.setFine(12.12345);
		try {
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getFine"), fixture.getClass().getMethod("setFine", double.class),fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JTextFieldAdapter.class, a.getClass()); //the adapter should be expected
			assertEquals("12.12345", ((JTextField) a.getDataComponent()).getText()); //the value of its component should be expected
			//(it called update() in its constructor)
			((JFormattedTextField) a.getDataComponent()).setText("12.0");//now try to change the component's value
			a.save(); //call save on the adapter
			assertEquals(12.0D, fixture.getFine()); //the value in the fixture object should have been updated			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}	
	//---------------------------Date
	

	/**Tests createAdapter on a java.util.date that should be readonly
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_DateReadonly() {
		
		
		try {
			Date d = dateFormat.parse("1/1/2009");
			Student fixture = new Student(123,"MyFirstName","", d, true, 0);
			
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getDateofbirth"), fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JDatePickerAdapter.class, a.getClass()); //the adapter should be a datepickeradapter
			assertEquals(d, dateFormat.parse(((JTextField) (a.getDataComponent())).getText())); //value has been set?
			((JTextField) (a.getDataComponent())).setText("20/5/2000"); //change the date of birth to something else
			a.save();
			assertEquals(d, fixture.getDateofbirth()); //readonly, so save should have had no effect, should still be d
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	/**Tests createAdapter on a java.util.date 
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_Date() {
		try {
			Date d = dateFormat.parse("1/1/2009");
			Student fixture = new Student(123,"MyFirstName","", d, true, 0);
			
			MethodProperty mp = new MethodProperty(fixture.getClass().getMethod("getDateofbirth"), fixture.getClass().getMethod("setDateofbirth", Date.class), fixture);
			Adapter a = AdapterFactory.createAdapter(mp);
			assertEquals(JDatePickerAdapter.class, a.getClass()); //the adapter should be a datepickeradapter
			assertEquals(d, dateFormat.parse(((JTextField)(a.getDataComponent())).getText())); //value has been set?
			((JTextField)(a.getDataComponent())).setText("20/5/2000"); //change the date of birth to something else
			a.save();
			assertEquals(dateFormat.parse("20/5/2000"), fixture.getDateofbirth()); //value should have changed
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	//---------------------------List/Array
	

	/**Tests createAdapter on a collection
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_Collection() {
		Paper fixture = new Paper("test"); 
		fixture.students.add(new Student(001,"student1","", new Date(), true, 0)); 
		fixture.students.add(new Student(002,"student2","", new Date(), true, 0)); 
		
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("students"), fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JListAdapter.class, a.getClass()); //the adapter should be a JListAdapter
			((JList) a.getDataComponent()).setSelectedIndex(0); //select the first student
			assertEquals(fixture.students.get(0), (Student) ((JList) a.getDataComponent()).getSelectedValue()); //should be the first student		
			((Student) ((JList) a.getDataComponent()).getSelectedValue()).firstname = "student42";//change the name of that student
			//save doesn't need to be called, because the student has already been updated 
			assertEquals("student42", fixture.students.get(0).firstname); //should have updated on the object
			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	
	/**Tests createAdapter on a simple 1d array
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_Array() {

		Student fixture = new Student(001,"fixture","", new Date(), true, 0); 
		fixture.friends[0] = ExampleGenerator.masseyStudents().get(0);	
		fixture.friends[1] = ExampleGenerator.masseyStudents().get(1);	
		//Student.friends has an annotation overriding the default adapter
		//and selecting a JListAdapter instead
		
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("friends"), fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JListAdapter.class, a.getClass()); //the adapter should be a JListAdapter
			((JList) a.getDataComponent()).setSelectedIndex(0); //select the first student
			assertEquals(fixture.friends[0], (Student) ((JList) a.getDataComponent()).getSelectedValue()); //should be the first student		
			((Student) ((JList) a.getDataComponent()).getSelectedValue()).firstname = "student42";//change the name of that student
			//save doesn't need to be called, because the student has already been updated 
			assertEquals("student42", fixture.friends[0].firstname); //should have updated on the object
			
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	
	//---------------------------Tree
	
	/**Tests createAdapter on a more complex array using tree adapter. Tree adapter is a requirement.
	 * The JTreeAdapter is readonly.
	 * 
	 * Test method for {@link juf.adapter.AdapterFactory#createAdapter(juf.Property)}.
	 */
	@Test
	public void testCreateAdapter_Tree() {
		Student fixture = ExampleGenerator.arraylistStudents().get(0); 
		
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("hierarchy"), fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(JTreeAdapter.class, a.getClass()); //the adapter should be a JTreeAdapter
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}
	}
	
	//---------------------------Arbitrary objects
	
	/**
	 * Arbitrary, user-defined objects (like Address, Student, or Paper) must be represented too. <br/>
	 * But we do not know how to represent these objects. The solution is to represent them as their toString, which
	 * every Object supports (letting the user overload this) and provide a means to editing the public members of that object.
	 * <p/>
	 * The GenericObjectAdapter is used for these cases, and simply provides another interface 
	 * for changing the bound object, using a JufForm and popup window. 
	 */
	@Test
	public void testCreateAdapter_Student() {
		Student fixture = new Student(001,"fixture","", new Date(), true, 0); 
		fixture.bestFriend = ExampleGenerator.arraylistStudents().get(0);
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("bestFriend"), fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(GenericObjectAdapter.class, a.getClass()); //the adapter should be a GenericObjectAdapter
			assertEquals(ExampleGenerator.arraylistStudents().get(0).toString(), ((JLabel) a.getDataComponent()).getText());
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}			
	}
	
	@Test
	public void testCreateAdapter_Paper() {
		Student fixture = new Student(001,"fixture","", new Date(), true, 0); 
		try {
			FieldProperty fp = new FieldProperty(fixture.getClass().getField("favoritePaper"), fixture);
			Adapter a = AdapterFactory.createAdapter(fp);
			assertEquals(GenericObjectAdapter.class, a.getClass()); //the adapter should be a GenericObjectAdapter
			assertEquals(fixture.favoritePaper.toString(), ((JLabel) a.getDataComponent()).getText());
		} 
		catch (Exception e){
			fail("unhandled exception" + e.toString());
		}	
	}
}
